/*
 * Copyright 2009 BioTeam Inc
 * 
 * Licensed under License GNU LESSER GENERAL PUBLIC LICENSE version 3.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.gnu.org/copyleft/lesser.html
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package net.bioteam.appweb.ws;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.activation.DataHandler;
import javax.activation.FileDataSource;

import net.bioteam.appweb.model.Parameter;
import net.bioteam.appweb.model.Program;
import net.bioteam.appweb.submit.ProgramValueBean;
import net.bioteam.common.user.User;

import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMFactory;
import org.apache.axiom.om.OMNamespace;
import org.apache.axiom.om.OMText;
import org.apache.axiom.om.util.Base64;
import org.apache.axis2.AxisFault;
import org.apache.axis2.Constants;
import org.apache.axis2.addressing.EndpointReference;
import org.apache.axis2.client.Options;
import org.apache.axis2.client.ServiceClient;
import org.apache.axis2.context.ConfigurationContext;
import org.apache.rampart.RampartMessageData;

public class WsClient
{
	public static final String NS = "http://net.bioteam.appweb/ws";
	protected static ServiceClient sc;
	protected OMFactory fac = null;
	protected OMNamespace omNs = null;

	
	//TODO make the initilaization effecient.
	public WsClient(User user, String serviceURL, ConfigurationContext ctx) throws AxisFault
	{

		// create the repository directory programatically
		if (ctx!=null)
			sc = new ServiceClient(ctx, null);
		else
		{
			sc=new ServiceClient(); //for server
			//http://wso2.org/library/585
			sc.getAxisService().addModuleref("rampart");
			//ctx= ListenerManager.defaultConfigurationContext;
		}
		sc.engageModule("rampart");
		Options options = new Options();
		options.setUserName(user.getName());
		options.setPassword(user.getPassword());
		options.setTo(new EndpointReference(serviceURL));
		// http://issues.apache.org/jira/browse/WSCOMMONS-399
		// options.setSoapVersionURI(org.apache.axiom.soap.SOAP12Constants.
		// SOAP_ENVELOPE_NAMESPACE_URI);
		// InputStream
		// in=Thread.currentThread().getContextClassLoader().getResourceAsStream
		// ("net/bioteam/webcommand/ws/client/policy.xml");
		// StAXOMBuilder builder = new StAXOMBuilder(in);
		// Policy policy= PolicyEngine.getPolicy(builder.getDocumentElement());
		options.setProperty(RampartMessageData.KEY_RAMPART_POLICY, getPolicy());
		options.setProperty(Constants.Configuration.ENABLE_MTOM, Constants.VALUE_TRUE);

		sc.setOptions(options);

		fac = OMAbstractFactory.getOMFactory();
		omNs = fac.createOMNamespace(NS, "ws");
	}

	public static final String policyStr = "<wsp:Policy wsu:Id=\"UToverHTTP\" xmlns:wsp=\"http://schemas.xmlsoap.org/ws/2004/09/policy\" xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\"><wsp:ExactlyOne><wsp:All><sp:SupportingTokens xmlns:sp=\"http://schemas.xmlsoap.org/ws/2005/07/securitypolicy\">\n                    <wsp:Policy>\n                        <sp:UsernameToken sp:IncludeToken=\"http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/Always\" />\n                    </wsp:Policy>\n                </sp:SupportingTokens></wsp:All></wsp:ExactlyOne></wsp:Policy>";

	private static org.apache.neethi.Policy getPolicy()
	{
		java.io.ByteArrayInputStream bais = new java.io.ByteArrayInputStream(policyStr.getBytes());
		return org.apache.neethi.PolicyEngine.getPolicy(bais);
	}

	/**
	 * 
	 * @param workdir
	 * @param bean
	 * @return jobid, workdir, cmdline, drmid in remote system.
	 * @throws AxisFault
	 */
	public String[] runProgram(ProgramValueBean bean) throws AxisFault
	{
		sc.getOptions().setAction("urn:executeProgram");
		OMElement method = fac.createOMElement("executeProgram", omNs);

		OMElement value = fac.createOMElement("programName", omNs);
		value.addChild(fac.createOMText(value, bean.getProgram().getName()));
		method.addChild(value);

		fillExecuteOMElement(method, bean);

		OMElement resp = sc.sendReceive(method);
		String jobid = null;
		String workdir = null;
		String cmdline=null;
		String drmid=null;
		for (Iterator iter = resp.getChildElements(); iter.hasNext();)
		{
			OMElement child = (OMElement) iter.next();
			if (child.getLocalName().equals("jobid"))
			{
				jobid = child.getText();
			} else if (child.getLocalName().equals("workdir"))
			{
				workdir = child.getText();
			} else if (child.getLocalName().equals("cmdline"))
			{
				cmdline=child.getText();
			} else if (child.getLocalName().equals("drmid"))
			{
				drmid=child.getText();
			}
		}
		String ret[] = { jobid, workdir, cmdline, drmid };

		return ret;
	}

	
	/**
	 * 
	 * @param workdir
	 * @param bean
	 * @return jobid, workdir, cmdline, drmid in remote system.
	 * @throws AxisFault
	 */
	public String[] runProgram1(ProgramValueBean bean) throws AxisFault
	{
		sc.getOptions().setAction("urn:executeProgram1");
		OMElement method = fac.createOMElement("executeProgram1", omNs);
		OMElement params=fac.createOMElement("Parameters", omNs);
		method.addChild(params);
		
		OMElement value = fac.createOMElement("programName", omNs);
		value.addChild(fac.createOMText(value, bean.getProgram().getName()));
		params.addChild(value);

		fillExecuteOMElement(params, bean);

		OMElement resp = sc.sendReceive(method);
		String jobid = null;
		String workdir = null;
		String cmdline=null;
		String drmid=null;
		for (Iterator iter = resp.getChildElements(); iter.hasNext();)
		{
			OMElement child = (OMElement) iter.next();
			if (child.getLocalName().equals("jobid"))
			{
				jobid = child.getText();
			} else if (child.getLocalName().equals("workdir"))
			{
				workdir = child.getText();
			} else if (child.getLocalName().equals("cmdline"))
			{
				cmdline=child.getText();
			} else if (child.getLocalName().equals("drmid"))
			{
				drmid=child.getText();
			}
		}
		String ret[] = { jobid, workdir, cmdline, drmid };

		return ret;
	}

	public OMElement fillExecuteOMElement(OMElement executeProgram, ProgramValueBean bean)
	{
		Program program = bean.getProgram();
		for (Map.Entry<String, String> entry : bean.getValueMap().entrySet())
		{
			String key = entry.getKey();
			String value = entry.getValue();
			Parameter param = program.getParameter(key);
			if (param == null)
			{
				if (key.startsWith("_infile"))
				{

					executeProgram.addChild(createFileElement( value, key));
				} else
				{
					OMElement pelement = fac.createOMElement(key, omNs);
					pelement.addChild(fac.createOMText(pelement, value));
					executeProgram.addChild(pelement);
				}
				continue;
			}
			switch (param.getType())
			{
			case BooleanType:

				if (value.equals("1") || value.equals("true") || value.equals("TRUE") || value.equals("Y") || value.equals("y"))
				{
					OMElement pelement = fac.createOMElement(key, omNs);
					pelement.addChild(fac.createOMText(pelement, "true"));
					executeProgram.addChild(pelement);
				} else
				{
					OMElement pelement = fac.createOMElement(key, omNs);
					pelement.addChild(fac.createOMText(pelement, "false"));
					executeProgram.addChild(pelement);
				}
				break;
			case MultipleSelectionType:
				String values[] = value.split(param.getSeparator());
				for (String v : values)
				{
					OMElement pelement = fac.createOMElement(key, omNs);
					pelement.addChild(fac.createOMText(pelement, v));
					executeProgram.addChild(pelement);
				}

				break;
			case InputFileType:
			case SequenceType:
				executeProgram.addChild(createFileElement(value, key));
				break;
			default:
				OMElement pelement = fac.createOMElement(key, omNs);
				pelement.addChild(fac.createOMText(pelement, value));
				executeProgram.addChild(pelement);
			}

		}

		return executeProgram;
	}

	private OMElement createFileElement(String filePath, String key)
	{
		File file = new File(filePath);
		if (file.exists())
		{
			// local file
			OMElement fileType = fac.createOMElement(key, omNs);

			OMElement filename = fac.createOMElement("fileName", omNs);
			filename.addChild(fac.createOMText(filename, file.getName()));
			fileType.addChild(filename);

			OMElement data = fac.createOMElement("binaryData", omNs);
			FileDataSource src = new FileDataSource(file);
			DataHandler dh = new DataHandler(src);
			OMText text = fac.createOMText(dh, true);
			data.addChild(text);
			fileType.addChild(data);
			return fileType;

		} else if (filePath.startsWith("/"))
		{
			// server file.
			OMElement fileType = fac.createOMElement(key, omNs);

			OMElement filename = fac.createOMElement("serverfile", omNs);
			filename.addChild(fac.createOMText(filename, filePath));
			fileType.addChild(filename);
			return fileType;

		} else
		{
			//usa
			// server file.
			OMElement fileType = fac.createOMElement(key, omNs);

			OMElement filename = fac.createOMElement("usa", omNs);
			filename.addChild(fac.createOMText(filename, filePath));
			fileType.addChild(filename);
			return fileType;
		}
	}

	public String[] getJobStatus(String jobid) throws AxisFault
	{
		sc.getOptions().setAction("urn:getJobStatus");
		OMElement method = fac.createOMElement("getJobStatus", omNs);
		OMElement value = fac.createOMElement("jobid", omNs);
		value.addChild(fac.createOMText(value, jobid));
		method.addChild(value);

		OMElement resp = sc.sendReceive(method);

		String status = null;
		String exitcode = null;
		String note=null;
		String drmid=null;
		for (Iterator iter = resp.getChildElements(); iter.hasNext();)
		{
			OMElement child = (OMElement) iter.next();
			if (child.getLocalName().equals("status"))
			{
				status = child.getText();
			} else if (child.getLocalName().equals("drmid"))
			{
				drmid = child.getText();
			} else if (child.getLocalName().equals("note"))
			{
				note=child.getText();
			} else if (child.getLocalName().equals("exitcode"))
			{
				exitcode=child.getText();
			}
		}
		String ret[] = {status, drmid, exitcode, note };
		return ret;

	}
	
	public String killJob(String jobid) throws AxisFault
	{
		sc.getOptions().setAction("urn:killJob");
		OMElement method = fac.createOMElement("killJob", omNs);
		OMElement value = fac.createOMElement("jobid", omNs);
		value.addChild(fac.createOMText(value, jobid));
		method.addChild(value);

		OMElement resp = sc.sendReceive(method);

		String message= null;
		message=resp.getFirstElement().getText();
		return message;

	}

	public List<String> listFiles(String jobid) throws AxisFault
	{
		sc.getOptions().setAction("urn:listFiles");
		OMElement method = fac.createOMElement("listFiles", omNs);
		OMElement value = fac.createOMElement("jobid", omNs);
		value.addChild(fac.createOMText(value, jobid));
		method.addChild(value);

		OMElement resp = sc.sendReceive(method);

		List<String> files = new LinkedList<String>();
		for (Iterator iter = resp.getChildElements(); iter.hasNext();)
		{
			OMElement child = (OMElement) iter.next();
			files.add(child.getText());
		}

		return files;

	}

	private OMText getFileText(String jobid, String filename) throws AxisFault
	{
		sc.getOptions().setAction("urn:getFile");
		OMElement method = fac.createOMElement("getFile", omNs);
		OMElement value = fac.createOMElement("jobid", omNs);
		value.addChild(fac.createOMText(value, jobid));
		method.addChild(value);

		value = fac.createOMElement("fileName", omNs);
		value.addChild(fac.createOMText(value, filename));
		method.addChild(value);

		OMElement resp = sc.sendReceive(method);
		OMElement content = (OMElement) resp.getFirstOMChild();
		OMText text = (OMText) content.getFirstOMChild();
		return text;
	}

	public void getFile(String jobid, String filename, OutputStream out) throws IOException
	{
		OMText text = getFileText(jobid, filename);
		if (text.isOptimized())
		{
			DataHandler dh = (DataHandler) text.getDataHandler();
			dh.writeTo(out);
		} else
		{
			String c = text.getText();
			Base64.decode(c, out);
		}

	}

	public InputStream getFile(String jobid, String filename) throws IOException
	{

		OMText text = getFileText(jobid, filename);
		if (text.isOptimized())
		{
			DataHandler dh = (DataHandler) text.getDataHandler();
			return dh.getInputStream();
		} else
		{
			String c = text.getText();
			return new ByteArrayInputStream(Base64.decode(c));
		}
	}

	private OMText getZipText(String jobid) throws AxisFault
	{
		sc.getOptions().setAction("urn:getJobFileAsZip");
		OMElement method = fac.createOMElement("getJobFileAsZip", omNs);
		OMElement value = fac.createOMElement("jobid", omNs);
		value.addChild(fac.createOMText(value, jobid));
		method.addChild(value);

		OMElement resp = sc.sendReceive(method);
		OMElement content = (OMElement) resp.getFirstOMChild();
		OMText text = (OMText) content.getFirstOMChild();
		return text;
	}

	public void getJobFileAsZip(String jobid, OutputStream out) throws IOException
	{
		OMText text = getZipText(jobid);
		if (text.isOptimized())
		{
			DataHandler dh = (DataHandler) text.getDataHandler();
			dh.writeTo(out);
		} else
		{
			String c = text.getText();
			Base64.decode(c, out);
		}
	}

	public InputStream getJobFileAsZip(String jobid) throws IOException
	{
		OMText text = getZipText(jobid);
		if (text.isOptimized())
		{
			DataHandler dh = (DataHandler) text.getDataHandler();
			return dh.getInputStream();
		} else
		{
			String c = text.getText();
			return new ByteArrayInputStream(Base64.decode(c));
		}
	}

}
